package com.my.study.utils;

import sun.misc.Unsafe;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * 计算对象占用内存大小，单位：MB
 *
 * @author: yidujun
 * @create: 2021/07/29 17:04
 */
public final class ObjectMemory {

    private static Unsafe UNSAFE;
    /** 对象头部的大小 */
    private static final int OBJECT_HEADER_SIZE = 8;
    /** 对象占用内存的最小值 */
    private static final int MINIMUM_OBJECT_SIZE = 8;
    /** 对象按多少字节的粒度进行对齐 */
    private static final int OBJECT_ALIGNMENT = 8;

    static {
        try {
            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            UNSAFE = (Unsafe) unsafeField.get(null);
        } catch (Throwable t) {
            UNSAFE = null;
        }
    }


    public static long sizeOf(Object obj) {
        Class<?> clazz = obj.getClass();
        // 判断是否为数值
        if (clazz.isArray()) {
            int baseOffset = UNSAFE.arrayBaseOffset(clazz);
            int indexScale = UNSAFE.arrayIndexScale(clazz);
            long size = baseOffset + (indexScale * Array.getLength(obj));
            if (size % OBJECT_ALIGNMENT != 0) {
                // 补齐8字节
                size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);
            }

            return Math.max(MINIMUM_OBJECT_SIZE, size);
        }
        else {
            // 如果数组对象则迭代遍历该对象的父类，找到最后一个非静态字段的偏移量
            for (Class<?> klazz = obj.getClass(); klazz != null; klazz = klazz.getSuperclass()) {
                long lastFieldOffset = -1;
                for (Field f : klazz.getDeclaredFields()) {
                    if (!Modifier.isStatic(f.getModifiers())) {
                        lastFieldOffset = Math.max(lastFieldOffset, UNSAFE.objectFieldOffset(f));
                    }
                }
                if (lastFieldOffset > 0) {
                    lastFieldOffset += 1;
                    if ((lastFieldOffset % OBJECT_ALIGNMENT) != 0) {
                        lastFieldOffset += OBJECT_ALIGNMENT - (lastFieldOffset % OBJECT_ALIGNMENT);
                    }
                    return Math.max(MINIMUM_OBJECT_SIZE, lastFieldOffset);
                }
            }
            // 该对象没有任何属性
            long size = OBJECT_HEADER_SIZE;
            if ((size % OBJECT_ALIGNMENT) != 0) {
                size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);
            }
            return Math.max(MINIMUM_OBJECT_SIZE, size);
        }
    }




}
